﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.CodeDom;
using System.Reflection;
using System.Collections;
using Doo.WebUI;
using Microsoft.Ajax.Utilities;

/*
 * 闭包函数集合
 * 用于创建一个闭包的脚本集合
 * 包含Function、Var、IDReference、ControlReference。
 * <doo:ScriptObject ID='ds' runat='server'>
 *  <Function name='add' params='ok,no'>
 *      return ok+no;
 *  </Function>
 *  <Var name="test" value="1" noquote="true" />
 *  <IDReference varname="textbox1" ControlID="textbox1"/>
 *  <ControlReference PropName="listview" ControlID="listview1"/>
 * </doo:ScriptObject>
 * 
 * 特殊处理：针对function，进行后期处理，以达到this指针效果
 * 
 * TODO:缓存输出
 */

namespace Doo.Controls
{
    /// <summary>
    /// 脚本函数集合
    /// </summary>
    [ToolboxData("<{0}:ScriptObject runat=server></{0}:ScriptObject>")]
    [ParseChildren(true, "Fields")]
    public class ScriptObject : System.Web.UI.Control
    {
        //脚本整合在core里面了

        #region 导出设置[暂未使用]
        /// <summary>
        /// 脚本对象唯一名
        /// 使用此名做为文件名和对象名
        /// </summary>
        internal string ObjectUniqueName { get; set; }

        /// <summary>
        /// 导出到单个文件
        /// </summary>
        internal bool ExportObject { get; set; }
        #endregion

        /// <summary>
        /// 表示全局域，无需转换
        /// </summary>
        public const string GlobalObject = "#";

        public bool IsDefault { get; set; }

        /// <summary>
        /// 集合
        /// </summary>
        [Category("Behavior"),
         Description("选择参数"),
        PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public Doo.Scripts.Fields.FieldCollection Fields
        {
            get;
            set;
        }

        /// <summary>
        /// 设置此值用于新建此类型的类并赋值。
        /// </summary>
        public string ScriptType { get; set; }

        /// <summary>
        /// 设置层级关系[框架用]
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ScriptObject ParentObject { get; set; }

        /// <summary>
        /// 是否禁用压缩脚本
        /// </summary>
        public bool DisableMinify { get; set; }

        /// <summary>
        /// 初始化后执行的函数名称
        /// </summary>
        public string InitFunctionName { get; set; }

        /// <summary>
        /// Sys.Load的函数名
        /// </summary>
        //public string LoadFunctionName { get; set; }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Context.Items["Doo.Scripts"] = this;
            this.Fields.Init(this);

            if (IsDefault)
            {
                if (NamingContainer is Doo.Scripts.IUseScriptObject)
                {
                    (NamingContainer as Doo.Scripts.IUseScriptObject).ScriptObject = this;
                }
            }
        }

        public Event GetEvent(string name)
        {
            return this.Fields[name] as Event;
        }

        public Function GetFunction(string name)
        {
            foreach (Doo.Scripts.Fields.Field func in this.Fields)
            {
                if (func == null || !(func is Function) || func.Name != name) continue;
                return func as Function;
            }
            return null;
        }

        public static ScriptObject Current
        {
            get
            {
                return HttpContext.Current.Items["Doo.Scripts"] as ScriptObject;
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            ScriptManager.GetCurrent(Page).Scripts.Add(new ScriptReference("Doo.WebUI.core.js", this.GetType().Assembly.FullName));

            Minifier minifier = new Minifier();
            
            //初始化事件
            List<Event> events = new List<Event>();
            
            List<Function> functions = new List<Function>();
            //生成脚本
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Doo.Scripts.InitObj('{0}',{{", this.ClientID);
            //设置中间的东西
            //格式：fields,__functions,__events,__init,__id
            TextWriter writer = new StringWriter(sb);
            bool hasfield = false;
            foreach (var field in this.Fields)
            {
                field.Init(this);
                if (field is Event)
                {
                    events.Add(field as Event);
                    continue;
                }
                if (field is Function)
                {
                    functions.Add(field as Function);
                    continue;
                }
                hasfield = true;
                field.RenderName(writer, Parent);
                writer.Write(":");
                if (!field.NoQuote) writer.Write("\"");
                field.RenderValue(writer, Parent);
                if (!field.NoQuote) writer.Write("\"");
                writer.Write(",");
            }

            if (hasfield) sb[sb.Length - 1] = '}';
            else
            {
                sb[sb.Length - 1] = 'n';
                sb.Append("ull");
            }

            sb.Append(",");

            #region Process Events
            if (events.Count > 0)
            {
                sb.Append("{");

                #region 处理事件数据，格式：{event1:[...],event2:0}。

                foreach (var evt in events)
                {
                    sb.Append(evt.Name);
                    sb.Append(":");
                    if (evt.Target == null || evt.Target.Count == 0)
                    {
                        sb.Append("0");
                    }
                    else
                    {
                        //添加数组
                        sb.Append("[");

                        foreach (var func in evt.Target)
                        {
                            sb.AppendFormat("\"{0}\",", func.GetClientReference());
                        }

                        sb.Remove(sb.Length - 1, 1);
                        sb.Append("]");
                    }
                    sb.Append(",");
                }
                #endregion

                sb[sb.Length - 1] = '}';

            }
            else
            {
                sb.Append("null");
            }

            #endregion

            sb.Append(",");

            #region Process Functions
            if (functions.Count > 0)
            {
                sb.Append("{");

                #region Functions

                foreach (var fun in functions)
                {
                    sb.Append(fun.Name);
                    sb.Append(":");
                    if (DisableMinify)
                    {
                        fun.RenderValue(writer, Parent);
                    }
                    else
                    {
                        fun.RenderMinifyValue(writer, Parent, minifier);
                    }
                    sb.Append(",");
                }

                #endregion

                sb[sb.Length - 1] = '}';
            }
            else
            {
                sb.Append("0");
            }
            #endregion

            sb.Append(',');

            #region Process ScriptType

            if (string.IsNullOrEmpty(ScriptType))
            {
                sb.Append("0");
            }
            else
            {
                sb.AppendFormat("'{0}'", ScriptType);
            }

            #endregion

            sb.Append(',');

            #region Process InitFunction

            sb.AppendFormat("'{0}'", this.InitFunctionName);

            #endregion

            #region 设置层级关系

            if (ParentObject != null)
            {

                sb.AppendFormat(",'{0}','{1}'", Parent.ID, ParentObject.ClientID);
                //ID用于findChild调用

            }

            #endregion

            sb.Append(");");

            //脚本生成完毕
            ScriptManager.RegisterStartupScript(this, this.GetType(), this.ClientID, sb.ToString(), true);

        }
    }

    #region 测试
    public class ScriptObjectControlBuilder : ControlBuilder
    {
        public ScriptObjectControlBuilder()
        {
            //this. = true;
        }

        private string id;

        public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, System.Collections.IDictionary attribs)
        {
            this.id = id;
            base.Init(parser, parentBuilder, type, tagName, id, attribs);
        }

        public override void ProcessGeneratedCode(System.CodeDom.CodeCompileUnit codeCompileUnit, System.CodeDom.CodeTypeDeclaration baseType, System.CodeDom.CodeTypeDeclaration derivedType, System.CodeDom.CodeMemberMethod buildMethod, System.CodeDom.CodeMemberMethod dataBindingMethod)
        {
            //Create Class
            CodeNamespace samples = new CodeNamespace("DooSO");
            samples.Imports.Add(new CodeNamespaceImport("System"));
            var targetClass = new CodeTypeDeclaration("test" + id);
            targetClass.IsClass = true;
            targetClass.TypeAttributes =
                TypeAttributes.Public | TypeAttributes.Sealed;
            samples.Types.Add(targetClass);
            codeCompileUnit.Namespaces.Add(samples);

            //Add Fields
            CodeMemberField textField = new CodeMemberField
            {
                Name = "text",
                Type = new CodeTypeReference(typeof(int)),
                Attributes = MemberAttributes.Private
            };
            targetClass.Members.Add(textField);

            //Add Property
            CodeMemberProperty cmp = new CodeMemberProperty
            {
                Name = "Text",
                Type = new CodeTypeReference(typeof(int)),
                Attributes = MemberAttributes.Private,
                HasGet = true
            };
            cmp.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(
                    new CodeThisReferenceExpression(), "text")));

            targetClass.Members.Add(cmp);

            derivedType.Members.Add(new CodeMemberField { Name = "wawawa", Type = new CodeTypeReference(typeof(string)), Attributes = MemberAttributes.Public });

            base.ProcessGeneratedCode(codeCompileUnit, baseType, derivedType, buildMethod, dataBindingMethod);
        }
    }
    #endregion
}
